﻿#region Apache License Version 2.0
/*----------------------------------------------------------------

Copyright 2024 DJ Zheng & ShangHai GM Nahefa Technology Co.,Ltd.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing permissions
and limitations under the License.

----------------------------------------------------------------*/
#endregion Apache License Version 2.0

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace Collector.Device
{
    public abstract class PulseProtocolPackage
    {
        private readonly string TAG = "PulseProtocolPackage:";
        private readonly UInt32 PULSE_FRAME_HEADER = 0x66BB66BB;
        private readonly UInt32 CONTROL_FRAME_HEADER = 0x5555AAAA;
        private readonly Object mLock = new Object();
        private ReaderWriterLockSlim mRWLock = new ReaderWriterLockSlim();
        IDeviceConnector mDeviceConnector = null;
        private UInt16 mSeqCheckSum = 0;
        private UInt32 mPulseFrameDataLostCount = 0;
        Queue<MessageRequest> mRequestQueue = new Queue<MessageRequest>();

        public delegate bool PulseDataDeliveredDelegate(byte[] frame, double data, PressureType type);
        private PulseDataDeliveredDelegate mDeliveredCallback;

        public enum PressureType
        {
            ePRESSURE_TYPE_PULSE,
            ePRESSURE_TYPE_AIR
        }

        public class MessageRequest {
            private byte expectedCode;
            private byte[] command;
            private byte[] payload;

            public byte ExpectedCode
            {
                get { return expectedCode; }
                set { expectedCode = value; }
            }

            public byte[] Command
            {
                get { return command; }
                set { command = value; }
            }

            public byte[] Payload
            {
                get { return payload; }
                set { payload = value; }
            }
        };

        public IDeviceConnector DeviceConnector
        {
            get { return mDeviceConnector; }
            set { mDeviceConnector = value; }
        }

        public PulseDataDeliveredDelegate DeliveredCallback
        {
            get { return mDeliveredCallback; }
            set { mDeliveredCallback = value; }
        }

        // 每次开始新的流程时需要进行重置配置
        public void ResetConfig()
        {
            mSeqCheckSum = 0;
            mPulseFrameDataLostCount = 0;
            mRequestQueue.Clear();
        }

        public bool SendCommand(byte[] command, byte expected, out string result)
        {
            MessageRequest request = new MessageRequest();
            request.ExpectedCode = expected;
            request.Command = (byte[])command.Clone();
            return SendCommand(request, out result);
        }

        public bool SendCommand(MessageRequest request, out string result, int timeout = 2000)
        {
            bool ret = false;

            System.Threading.Monitor.Enter(mLock);
            mRequestQueue.Enqueue(request);
            ret = SendCommandLock(request.Command, timeout);
            if(ret)
            {
                result = ConvertFuncs.ByteArrayToHexString(request.Payload);
            }
            else
            {
                result = "";
            }

            mRequestQueue.Dequeue();
            System.Threading.Monitor.Exit(mLock);

            return ret;
        }

        private bool SendCommandLock(byte[] command, int timeout)
        {
            bool signalled = false;
            System.Threading.Monitor.Enter(mLock);
            if (mDeviceConnector == null)
            {
                Log.Debug(TAG + "SendCommandLock IDeviceConnector isnull");
                System.Threading.Monitor.Exit(mLock);
                return false;
            }

            mDeviceConnector.SendData(command);
            signalled = System.Threading.Monitor.Wait(mLock, timeout, false);
            if (signalled)
            {
                // Log.Debug("Thread[{0:d4}]: Monitor Wait released!!!", Thread.CurrentThread.GetHashCode());
            }
            else
            {
                Log.Debug(TAG + "Thread[{0:d4}]: Monitor Wait command [{1}] timeout", Thread.CurrentThread.GetHashCode(), ConvertFuncs.ByteArrayToHexString(command));
            }

            // Log.Debug("receive data 3 queue count {0}", mRequestQueue.Count);
            System.Threading.Monitor.Exit(mLock);
            
            return signalled;
        }

        public void SerialPortReceivedCallback(object sender, PortDataReciveEventArgs e)
        {
            // 对接收到的脉象数据进行解析
            //Log.Debug(string.Format("e.Data :{0}",ConvertFuncs.ByteArrayToHexString(e.Data)));
            IPulseCollectorFactory collector = e.CollectorInstanceGet();
            int byteToRead = e.Data.Length;
            if (byteToRead == 0)
            {
                //Log.Debug("receive data is null");
                return;
            }

            byte[] receiveData = new byte[byteToRead];
            Array.Copy(e.Data, receiveData, byteToRead);
            for(int i=0; i< byteToRead; )
            {
                UInt32 header = BitConverter.ToUInt32(receiveData, i);
                // header + length + payload
                int offset = 4 + 1 + receiveData[i+ IDeviceProtocolShape.FRAME_FIELD_LENGTH_OFFSET];
                if (header == PULSE_FRAME_HEADER)
                {

                    PulseFrameProcessing(receiveData, i, byteToRead, collector);
                    i += offset;
                }
                else if(header == CONTROL_FRAME_HEADER)
                {
                    ControlFrameProcessing(receiveData, i, byteToRead, collector);
                    i += offset;
                }
                else
                {
                    Log.Debug(TAG + "SerialPortReceivedCallback() The received data has some anomalies, current index {0} total size {1}", i, byteToRead);
                    i++;
                }
            }
        }

        /**
         * 脉压帧数据后处理
         * 
         * @param frame 来自当前串口事务的所接收的原始数据
         * @param offset 表示当前需要处理的脉压数据所在原始数据中的索引位置
         * @param size 原始数据@c frame的数据大小
         * @param collector 处理帧事务的对象
         */
        private void PulseFrameProcessing(byte[] frame, int offset, int size, IPulseCollectorFactory collector)
        {
            byte payload_size = frame[offset + IDeviceProtocolShape.FRAME_FIELD_LENGTH_OFFSET];
            int seq_h_offset = offset + 4 + (int)payload_size;
            UInt16 seqChecked = (UInt16)((frame[seq_h_offset-1]) | (frame[seq_h_offset] << 8));
            // 第一次收到data frame，初始赋值seq, 或者当设备响应了脉压指令，seqChecked被重新置0时
            if ((mSeqCheckSum == 0 || seqChecked == 0 ) && mSeqCheckSum != seqChecked)
            {
                mSeqCheckSum = seqChecked;
            }
            // 设备seq为0有两种情况：1， 设备启动和自计数反转后从0开始计算； 2，设备响应了开启脉压指令，计数从0开始
            // 排除seq不为0的情况下， 接收seq和记录seq不一致即为丢包。
            else if (seqChecked !=0 && mSeqCheckSum != seqChecked)
            {

                Log.Debug("Current_received_Data :" + seqChecked +
                        ", Actual_record_Data :" + mSeqCheckSum +
                        ", ACKDataLostCount :" + (mPulseFrameDataLostCount + 1) +
                        ", " + string.Format("0x{0:X2},0x{1:X2}", frame[seq_h_offset - 1], frame[seq_h_offset]));

                mPulseFrameDataLostCount++;
                mSeqCheckSum = seqChecked;
            }

            // Prepare Next seq.
            mSeqCheckSum++;

            double pPData = 0.0;
            mRWLock.TryEnterWriteLock(500);
            try {
                int framesize = frame[offset+ IDeviceProtocolShape.FRAME_FIELD_LENGTH_OFFSET] + 5 /* 5 -> header(4) + len(1) */;
                byte[] singleFrame = new byte[framesize];
                Array.Copy(frame, offset, singleFrame, 0, framesize);

                //这里更新脉压数据
                if (mDeliveredCallback != null)
                {                    
                    //Log.Debug("frame: " + ConvertFuncs.ByteArrayToHexString(currentFrame) + ", size:{0} raw: {1}", framesize, ConvertFuncs.ByteArrayToHexString(frame));
                    mDeliveredCallback(singleFrame, pPData, PressureType.ePRESSURE_TYPE_PULSE);
                }

                if (collector != null)
                {                    
                    collector.PulseFrameProcessing(singleFrame);
                }
            }
            finally
            {
                mRWLock.ExitWriteLock();
            }

        }

        private void ControlFrameProcessing(byte[] frame, int offset, int size, IPulseCollectorFactory collector)
        {
            int len = 4 + 1 + frame[offset + IDeviceProtocolShape.FRAME_FIELD_LENGTH_OFFSET];
            byte[] control = new byte[len];
            Array.Copy(frame, offset, control, 0, len);

            // Event A5 is air pressure, will ignore it.
            if (control[IDeviceProtocolShape.FRAME_FIELD_CMD_OFFSET] == (byte)DeviceEvent.eDEV_EVENT_AIR_PRESSURE)
            {
                // 这是气压数据 单独处理
                // 这里更新气压数据
                mRWLock.TryEnterWriteLock(500);
                try
                {
                    // 交付给UI进行数据绘图
                    if (mDeliveredCallback != null)
                    {
                        double pressure = ((control[6]) | (control[7] << 8));
                        //Log.Debug("frame: " + ConvertFuncs.ByteArrayToHexString(frame));
                        mDeliveredCallback(control, pressure, PressureType.ePRESSURE_TYPE_AIR);
                    }

                    // 交付给具体的实例对象进行后续处理
                    if (collector != null)
                    {
                        collector.AirFrameProcessing(control);
                    }
                }
                finally
                {
                    mRWLock.ExitWriteLock();
                }
            }
            else
            {
                Monitor.Enter(mLock);
                //foreach(var it in mRequestQueue)
                //{
                    //Log.Debug(TAG + "current queue command:{0}", ConvertFuncs.ByteArrayToHexString(it.Command));
                //}

                if (mRequestQueue.Count > 0)
                {
                    MessageRequest request = mRequestQueue.Peek();
                    if (request.ExpectedCode == control[IDeviceProtocolShape.FRAME_FIELD_CMD_OFFSET])
                    {
                        // 同步，通知指令完成状态
                        request.Payload = (byte[])control.Clone();
                        Monitor.Pulse(mLock);
                    }
                    else
                    {
                        if (collector != null)
                        {
                            // 上报事件
                            collector.ControlFrameProcessing(control);
                        }
                    }
                }
                else
                {
                    if (collector != null)
                    {
                        // 上报事件
                        collector.ControlFrameProcessing(control);
                    }
                }

                Monitor.Exit(mLock);
            }
        }
    }
}
